package com.lhkj.ct.meta.modules.scheduling.service.impl;

import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import com.lhkj.ct.base.enums.GlobalStatus;
import com.lhkj.ct.base.mybatis.extension.service.impl.BatchHandleServiceImpl;
import com.lhkj.ct.base.utils.Validate;
import com.lhkj.ct.meta.modules.scheduling.common.enums.ScheduleStatus;
import com.lhkj.ct.meta.modules.scheduling.mapper.SchedulingMapper;
import com.lhkj.ct.meta.modules.scheduling.model.dto.ScheduleDto;
import com.lhkj.ct.meta.modules.scheduling.model.dto.ScheduleSearch;
import com.lhkj.ct.meta.modules.scheduling.model.dto.SchedulingDoctor;
import com.lhkj.ct.meta.modules.scheduling.model.dto.SchedulingTime;
import com.lhkj.ct.meta.modules.scheduling.model.entity.TblScheduling;
import com.lhkj.ct.meta.modules.scheduling.model.vo.ScheduleMonthVo;
import com.lhkj.ct.meta.modules.scheduling.model.vo.SchedulingDateVo;
import com.lhkj.ct.meta.modules.scheduling.model.vo.SchedulingDoctorVo;
import com.lhkj.ct.meta.modules.scheduling.service.SchedulingService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 排班记录 服务实现类
 * </p>
 *
 * @author jobob
 * @since 2023-10-27
 */
@Service
public class SchedulingServiceImpl extends BatchHandleServiceImpl<SchedulingMapper, TblScheduling> implements SchedulingService {

    /**
     * 排班管理 - 日历列表
     */
    @Override
    public List<ScheduleMonthVo> selectAllByMonth(ScheduleSearch search) {
        if (null == search.getSearchDate()) {
            search.setSearchDate(LocalDate.now());
        }
        return getBaseMapper().selectAllByMonth(search);
    }

    /**
     * 排班管理 - 查看某天排班详情
     */
    @Override
    public List<TblScheduling> selectAllByDate(ScheduleSearch search) {
        Validate.notNull(search.getSearchDate(), "日期不能为null");
        return getBaseMapper().selectAllByDate(search);
    }

    /**
     * 排班管理 - 新增排班
     */
    @Override
    @Transactional
    public boolean save(ScheduleDto scheduleDto) {
        final long between = scheduleDto.getStartDate().until(scheduleDto.getEndDate(), ChronoUnit.DAYS);
        Validate.isFalse(between < 0, "截止日期不能早于开始日期");
        Validate.isFalse(isTimeConflict(scheduleDto.getSchedulingTimes()), "排班时间段存在重合！");
        final Set<Integer> excludes = scheduleDto.getExcludes();
        boolean hasExclude = !excludes.isEmpty();
        final List<SchedulingDoctor> schedulingDoctors = scheduleDto.getSchedulingDoctors();
        List<TblScheduling> list = new ArrayList<>();
        List<LocalDate> dates = new ArrayList<>();
        for (long i = 0; i < between + 1; i++) {
            LocalDate cursor = scheduleDto.getStartDate().plusDays(i);
            if (hasExclude && excludes.contains(cursor.getDayOfWeek().getValue())) {
                continue;
            }
            dates.add(cursor);
            for (SchedulingDoctor schedulingDoctor : schedulingDoctors) {
                for (SchedulingTime schedulingTime : scheduleDto.getSchedulingTimes()) {
                    list.add(TblScheduling.builder()
                            .scheDept(schedulingDoctor.getDeptId())
                            .scheDoc(schedulingDoctor.getDoctorId())
                            .scheNum(schedulingTime.getNum())
                            .scheDate(cursor)
                            .startTime(schedulingTime.getStart())
                            .endTime(schedulingTime.getEnd())
                            .sStatus(ScheduleStatus.NORMAL)
                            .build());
                }
            }
        }
        Validate.notEmpty(dates, "没有符合条件的日期");
        Set<TblScheduling> repeatList = getBaseMapper().searchRepeat(schedulingDoctors, dates, scheduleDto.getSchedulingTimes());
        Map<String, String> collect = repeatList.stream().collect(Collectors.groupingBy(
                TblScheduling::getScheDoc,
                Collectors.mapping(
                        e -> e.getScheDate().toString(),
                        Collectors.joining(",", "[", "]")
                )
        ));
        Validate.isTrue(collect.isEmpty(), collect.entrySet().stream()
                .map(m -> String.format("医生[%s]在日期[%s]的排班存在时间冲突！", m.getKey(), m.getValue()))
                .collect(Collectors.joining("</br>")));
        return saveBatch(list);
    }

    @Override
    public int updateById(TblScheduling entity) {
        return super.updateById(entity);
    }

    @Override
    public boolean cancelByDate(LocalDate date) {
        Long l = getBaseMapper().countReducedNum(date);
        Validate.isFalse(l > 0, "%tF有待治疗的预约，如要取消请先联系患者取消预约！", date);
        LambdaUpdateWrapper<TblScheduling> updateWrapper = new LambdaUpdateWrapper<TblScheduling>()
                .set(TblScheduling::getStatus, ScheduleStatus.CANCEL)
                .eq(TblScheduling::getScheDate, date)
                .eq(TblScheduling::getStatus, GlobalStatus.NORMAL);
        return SqlHelper.retBool(getBaseMapper().update(null, updateWrapper));
    }

    @Override
    @Transactional
    public int cancel(Collection<?> ids) {
        List<TblScheduling> list = getBaseMapper().getByIds(ids);
        for (TblScheduling scheduling : list) {
            Validate.validState(scheduling.getReducedNum() <= 0, "医生:%s,排班时间:%tF %tT~%tT有待治疗的预约，如要取消请先联系患者取消预约",
                    scheduling.getScheDoc(), scheduling.getScheDate(), scheduling.getStartTime(), scheduling.getEndTime());
        }
        return getBaseMapper().update(null,
                new LambdaUpdateWrapper<TblScheduling>()
                        .set(TblScheduling::getStatus, ScheduleStatus.CANCEL)
                .in(TblScheduling::getId, ids));
    }

    @Override
    public int stop(Collection<?> ids) {
        return getBaseMapper().update(null, new LambdaUpdateWrapper<TblScheduling>()
                .set(TblScheduling::getStatus, ScheduleStatus.SUSPEND)
                .in(TblScheduling::getId, ids)
                .eq(TblScheduling::getStatus, ScheduleStatus.NORMAL));
    }

    @Override
    public int restore(Collection<?> ids) {
        return getBaseMapper().update(null, new LambdaUpdateWrapper<TblScheduling>()
                        .set(TblScheduling::getStatus, ScheduleStatus.NORMAL)
                .in(TblScheduling::getId, ids)
                .eq(TblScheduling::getStatus, ScheduleStatus.SUSPEND));
    }

    @Override
    public List<SchedulingDateVo> selectScheDates(ScheduleSearch search) {
        return getBaseMapper().selectScheDates(search);
    }

    @Override
    public List<SchedulingDoctorVo> selectScheDoctor(ScheduleSearch search) {
        Validate.notNull(search.getSearchDate(), "日期不能为null");
        return getBaseMapper().selectScheDoctor(search);
    }

    @Override
    public List<TblScheduling> selectScheTimes(ScheduleSearch search) {
        Validate.notNull(search.getSearchDate(), "日期不能为null");
        Validate.notNull(search.getDoctor(), "医生不能为null");
        return getBaseMapper().selectScheTimes(search);
    }

    private boolean isTimeConflict(List<SchedulingTime> list) {
        // 先按照开始时间排序
        for (int i = 0; i < list.size() - 1; i++) {
            SchedulingTime obj1 = list.get(i);
            SchedulingTime obj2 = list.get(i + 1);
            if (obj1.getStart().isAfter(obj2.getEnd())) {
                // 如果两个对象的时间有重叠，则返回true
                return true;
            }
        }
        return false;
    }

}
